package org.test.java;

import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;

import com.google.gson.Gson;

public class ShuaTa implements Runnable {

	private String _sid;
	Properties props;
	String[] stageIds = new String[] { "6", "5", "4", "3" };
	static String logFile = "ShuaTa.log";
	static int sleepTime = 5; // minutes
	static long cacheTimeOut = 3600; // seconds
	static BattleFailedCache cache = new BattleFailedCache();

	public ShuaTa(String _sid, Properties props) {
		this._sid = _sid;
		this.props = props;
		if (props.containsKey("stages")) {
			String stages = props.getProperty("stages");
			stageIds = stages.split(",");
		}
		if (props.containsKey("shuaTaLogFile")) {
			logFile = props.getProperty("shuaTaLogFile");
		}
		if (props.containsKey("shuaTaSleepTime")) {
			sleepTime = Integer.parseInt(props.getProperty("shuaTaSleepTime"));
		}
		if (props.containsKey("cacheTimeOut")) {
			cacheTimeOut = Integer.parseInt(props.getProperty("cacheTimeOut"));
		}
	}

	public void run() {
		int temp = 0;
		while (true) {
			try {
				try {
					if (temp >= 3)
						Thread.sleep(sleepTime * 60 * 1000);
					temp++;
				} catch (InterruptedException e1) {
					e1.printStackTrace();
					try {
						log(e1.getMessage());
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
				try {
					// 查询可以刷的map statge id和layer
					// 先查询再判断体力的原因，有可能有已刷完可以重置的塔
					int[] mapInf = getMapInfo();
					if (!judgeTili(_sid, App.energyControl, logFile)
							|| !judgeChips(_sid, 2, logFile)) {
						log("体力不够或者chip已满。等待刷塔。");
						continue;
					}
					if (mapInf == null)
						continue;
					int stage = mapInf[0];
					int layer = mapInf[1];
					getLayerInfoAndBattle(stage, layer);
				} catch (Exception e) {
					e.printStackTrace();
					try {
						log(e.getMessage());
					} catch (IOException e1) {
						e1.printStackTrace();
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	private static int getInt(Map map, String key) {
		return (Double.valueOf(map.get(key).toString())).intValue();
	}

	/**
	 * 2 宝箱 3 怪物 4入口 5出口
	 * 
	 * @param stage
	 * @param layer
	 * @throws IOException
	 */
	private boolean getLayerInfoAndBattle(int stage, int layer)
			throws IOException {
		/**
		 * 第一次请求为了清除当前楼层的怪
		 */
		Map result = httpRequest(_sid, "maze.php", "Info", "MapStageId="
				+ stage + "&Layer=" + layer);
		int status = getInt(result, "status");
		if (status == 0)
			return false;
		Map data = (Map) result.get("data");
		Map map = (Map) data.get("Map");
		List items = (List) map.get("Items");
		for (int i = 0; i < items.size(); i++) {
			Object item = items.get(i);
			int type = (Double.valueOf(item.toString())).intValue();
			switch (type) {
			case 2:
			case 3:
			case 5:
				if (notInBattleFailedCache(stage, layer, i))
					battle(stage, layer, i);
				break;
			}
		}

		/**
		 * 再请求一次为了判断当前层是否清空（如果isFinish表示5被清除掉的话）
		 */
		result = httpRequest(_sid, "maze.php", "Info", "MapStageId=" + stage
				+ "&Layer=" + layer);
		status = getInt(result, "status");
		if (status == 0)
			return false;
		data = (Map) result.get("data");
		map = (Map) data.get("Map");
		String isFinish = map.get("IsFinish").toString();
		items = (List) map.get("Items");
		boolean noMounst = true;
		for (int i = 0; i < items.size(); i++) {
			Object item = items.get(i);
			int type = (Double.valueOf(item.toString())).intValue();
			switch (type) {
			case 2:
			case 3:
				noMounst = false;
				break;
			}
		}
		// 判断的当前层是否已完成
		if (noMounst && "true".equals(isFinish)) {
			log("Finish for stage=" + stage + " and layer=" + layer
					+ "; result=\n" + data);
			// 判断当前塔是否已完成
			StageInfo info = getStageInfo(String.valueOf(stage));
			if (info.isContinue || info.result == null) {
				// 已清空，不管是否有重置都不继续下一层，等待下次进入判断
				return true;
			}
			boolean ok = getLayerInfoAndBattle(stage, layer + 1);
			if (!ok)
				addToFailedCache(stage, layer, 0);
		}
		return true;
	}

	private void battle(int stage, int layer, int i) throws IOException {
		Map map = httpRequest(_sid, "maze.php", "Battle",
				"manual=0&MapStageId=" + stage + "&Layer=" + layer
						+ "&ItemIndex=" + i);
		int status = getInt(map, "status");
		if (status == 0)
			return;
		Map data = (Map) map.get("data");
		log(data.toString());
		int win = getInt(data, "Win");
		if (win != 1)
			addToFailedCache(stage, layer, i);
	}

	private void addToFailedCache(int stage, int layer, int i)
			throws IOException {
		log("刷塔战斗失败。" + stage + " " + layer + " " + i);
		cache.addToCache(stage, layer, i);
		log(cache.toString());
	}

	private boolean notInBattleFailedCache(int stage, int layer, int i) {
		return !cache.inCache(stage, layer, i);
	}

	private boolean inBattleFailedCache(int stage) {
		return cache.stageInCache(stage);
	}

	/**
	 * 
	 * @return new int[]{stage, layer}
	 * @throws IOException
	 */
	private int[] getMapInfo() throws IOException {
		int[] result = null;
		for (String stage : stageIds) {
			if (inBattleFailedCache(Integer.parseInt(stage)))
				continue;

			StageInfo info = getStageInfo(stage);
			if (info.isContinue)
				continue;
			if (result == null)
				result = info.result;
		}
		return result;
	}

	class StageInfo {
		int[] result;
		boolean isContinue = false;
	}

	private StageInfo getStageInfo(String stage) throws IOException {
		StageInfo info = new StageInfo();
		int[] result = null;
		Map stageResult = httpRequest(_sid, "maze.php", "Show", "MapStageId="
				+ stage);
		Map data = (Map) stageResult.get("data");
		int clear = getInt(data, "Clear");
		int freeReset = getInt(data, "FreeReset");
		int layer = getInt(data, "Layer");
		if (clear == 1 && freeReset == 0) {
			log("已清空，无重置 for stage=" + stage);
			info.isContinue = true;
		}
		if (clear == 1 && freeReset == 1) {
			log("已清空，有重置 for stage=" + stage);
			resetMap(Integer.parseInt(stage));
		}
		if (clear == 0 && result == null) {
			log("未清空 for stage=" + stage);
			result = new int[] { Integer.parseInt(stage), layer };
		}
		info.result = result;
		return info;
	}

	private void resetMap(int stageId) throws IOException {
		httpRequest(_sid, "maze.php", "Reset", "MapStageId=" + stageId);
		log("已重置stage。" + stageId);
	}

	private static Map httpRequest(String _sid, String urlTarget,
			String action, String content) throws IOException {
		return httpRequestWithLogFile(_sid, urlTarget, action, content, logFile);
	}

	public static Map httpRequestWithLogFile(String _sid, String urlTarget,
			String action, String content, String logFile) throws IOException {
		HttpClient client = new HttpClient();
		String url = "http://s7.mysticalcard.com/"
				+ urlTarget
				+ "?do="
				+ action
				+ "&v=856&phpp=ANDROID&phpl=ZH_CN&pvc=1.3.0&pvb=2013-06-19%2016%3A27%3A25";
		print(logFile, url + "\t" + content);
		PostMethod post = new PostMethod(url);
		try {
			post.setRequestHeader("Cookie", "_sid=" + _sid);
			RequestEntity entity = new StringRequestEntity(content,
					"application/x-www-form-urlencoded", "UTF8");
			post.setRequestEntity(entity);
			int status = client.executeMethod(post);
			if (status != 200)
				throw new HttpException("status = " + status);
			String result = App.readGzip(post);
			Gson gson = new Gson();
			Map map = (Map) gson.fromJson(result, Map.class);
			return map;
		} finally {
			post.releaseConnection();
		}
	}

	public static boolean judgeTili(String _sid, int limit, String logFile)
			throws IOException {
		Map map = httpRequest(_sid, "user.php", "GetUserinfo", "");
		Map data = (Map) map.get("data");
		log(data.toString());
		if (data.get("Energy") instanceof String) {
			log("Energy返回值竟然不是Double!!!怎么设计的这个。\n" + data.toString());
			return false;
		}
		int energy = Double.valueOf(data.get("Energy").toString()).intValue();
		print(logFile, "体力值：" + energy);
		return energy >= limit;
	}

	/**
	 * type 1 探索 type 2 打开任意宝箱 type 3 自由切磋
	 * 
	 * @param _sid
	 * @return
	 * @throws IOException
	 */
	public static boolean judgeChips(String _sid, int expType, String logFile)
			throws IOException {
		Map map = httpRequest(_sid, "chip.php", "GetUserChip", "");
		Map data = (Map) map.get("data");
		for (Object v : data.values()) {
			Map value = (Map) v;
			int num = getInt(value, "num");
			int type = getInt(value, "type");
			if (type == expType && num == 0)
				return true;
		}
		print(logFile, "chip已满，无需"
				+ (expType == 1 ? "探索" : (expType == 2 ? "刷塔" : "自由切磋")) + "。");
		return false;
	}

	private static void log(String result) throws IOException {
		print(logFile, result);
	}

	public static void print(String logFile, String result) throws IOException {
		if (result == null)
			result = "传进来的日志信息为空值啊！！";
		FileOutputStream fos = new FileOutputStream(logFile, true);
		try {
			fos.write((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
					.format(new Date()) + "\t").getBytes());
			fos.write(result.getBytes());
			fos.write("\n".getBytes());
		} finally {
			fos.close();
		}
	}

}

class BattleFailedCache {
	List<StageInfo> cache = new ArrayList();

	@Override
	public String toString() {
		return "BattleFailedCache [cache=" + cache + "]";
	}

	class StageInfo {
		private int stage;
		private int layer;
		private int index;
		private long createTime;

		public StageInfo(int stage, int layer, int index) {
			super();
			this.stage = stage;
			this.layer = layer;
			this.index = index;
			this.createTime = System.currentTimeMillis() / 1000;
		}

		@Override
		public String toString() {
			return "StageInfo [stage=" + stage + ", layer=" + layer
					+ ", index=" + index + "]";
		}

		@Override
		public boolean equals(Object obj) {
			if (!(obj instanceof StageInfo))
				return false;
			StageInfo o = (StageInfo) obj;
			return stage == o.stage && layer == o.layer && index == o.index;
		}

		public boolean stageEquals(StageInfo o) {
			return stage == o.stage;
		}
	}

	public void addToCache(int stage, int layer, int index) {
		cache.add(new StageInfo(stage, layer, index));
	}

	public boolean stageInCache(int stage) {
		for (int i = 0; i < cache.size(); i++) {
			StageInfo s = cache.get(i);
			if (s.stage == stage) {
				long currentTime = System.currentTimeMillis();
				if (currentTime - s.createTime >= ShuaTa.cacheTimeOut) {
					cache.remove(i);
					return false;
				}
				return true;
			}
		}
		return false;
	}

	public boolean inCache(int stage, int layer, int index) {
		for (int i = 0; i < cache.size(); i++) {
			StageInfo s = cache.get(i);
			if (new StageInfo(stage, layer, index).equals(s)) {

				long currentTime = System.currentTimeMillis();
				if (currentTime - s.createTime >= ShuaTa.cacheTimeOut) {
					cache.remove(i);
					return false;
				}
				return true;
			}
		}
		return false;
	}

}
